﻿using Futaba.SBus.Telemetry.Configuration;
using System;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;

namespace Futaba.SBus.Telemetry
{
	/// <summary>
	/// Represents an instance of a <see cref="Configuration.Sensor"/>, which consumes
	/// a slot within the telemetry-configuration.
	/// </summary>
	/// <remarks>
	/// Slots can be detected by parsing the FLI file provided on the SDCard of the transmitter
	/// </remarks>
	public class Slot : NotifyPropertyChangedBase, ISlot
  {

		/// <summary>
		/// Creates a default instance of this class
		/// </summary>
		public Slot()
		{
			ParseContext = new SlotValueParserContext();
		}

		private Sensor _internalSensor;
		/// <summary>
		/// The sensor which provides this slot
		/// (ignored during serialization)
		/// </summary>
		[XmlIgnore]
		public virtual Sensor Sensor
		{
			get { return _internalSensor; }
			set
			{
				if (Equals(_internalSensor, value))
					return;
				_internalSensor = value;
				SensorType = value != null ? value.SensorType : SensorType.Unknown;
			}
		}

		/// <summary>
		/// The base type of this sensor (only used for XML, so we don't have to serialize the complete <see cref="Sensor"/> property)
		/// </summary>
		[XmlAttribute("sensorType")]
		public virtual SensorType SensorType { get; set; }

		/// <summary>
		/// The slot of this sensor
		/// </summary>
		[XmlAttribute("slot")]
		public virtual SlotIdType SlotId { get; set; }

		/// <summary>
		/// The multiplies of the transported value for this instance. This can be different
		/// for the different <see cref="SensorParameter"/> instances declared in a S.BUS stream
		/// </summary>
		/// <remarks>
		/// When using multiple RPM sensors, for example, a magnetic sensor usually has a different
		/// ratio to an optical sensor, or if you want to account for a gear-ratio
		/// </remarks>
		[XmlAttribute("valueMultiplyer")]
		public virtual double ValueMultiplyer { get; set; }

		/// <summary>
		/// A human readable display name of this instance of a slot
		/// </summary>
		[XmlAttribute("displayName")]
		public virtual string DisplayName { get; set; }

		/// <summary>
		/// The <see cref="SensorParameter.ParameterName"/> of the parameter within the <see cref="Sensor"/>
		/// this slot references
		/// </summary>
		[XmlAttribute("parameterName")]
		public virtual string ParameterName { get; set; }

		/// <summary>
		/// The configuration/parameters of this slot
		/// </summary>
		private SensorParameter _internalParameter;
		/// <summary>
		/// The parameter provides additional parsing and displaying context for this slot
		/// (ignored during serialization)
		/// </summary>
		[XmlIgnore]
		public virtual SensorParameter Parameter
		{
			get { return _internalParameter; }
			set
			{
				if (Equals(_internalParameter, value))
					return;
				_internalParameter = value;
				ParameterName = value != null ? value.ParameterName : null;
			}
		}

		private double? _internalCurrentValue;
		/// <summary>
		/// The current/last received value of this slot (not serialized)
		/// </summary>
		/// <seealso cref="Sequence"/>
		//[XmlAttribute("currentValue")]
		[XmlIgnore]
		public virtual double? CurrentValue
		{
			get { return _internalCurrentValue; }
			set
			{
				if (Equals(_internalCurrentValue, value))
					return;
				_internalCurrentValue = value;
				RaisePropertyChanged(); //RaisePropertyChanged("CurrentValue");
			}
		}

		/// <summary>
		/// The sequence of the <see cref="CurrentValue"/> (not serialized)
		/// </summary>
		/// <remarks>
		/// Futaba protocol seems to cycle sequence bytes for every couple of packets, but
		/// all <see cref="SlotId">slots</see> with the same Sequence were received in the same packet
		/// </remarks>
		//[XmlAttribute("sequence")]
		[XmlIgnore]
		public virtual byte Sequence { get; set; }

		/// <summary>
		/// The point in time of the <see cref="CurrentValue"/> (not serialized)
		/// </summary>
		/// <remarks>
		/// All slots within the same packet are assigned the same ReceivedUtc
		/// </remarks>
		//[XmlAttribute("receivedUtc")]
		[XmlIgnore]
		public virtual DateTime ReceivedUtc { get; set; }

		/// <summary>
		/// The Parse-Context required by the <see cref="SlotValueParser"/> (not serialized)
		/// </summary>
		[XmlIgnore]
		public virtual SlotValueParserContext ParseContext { get; protected set; }

		/// <summary>
		/// Returns the numerical representation of this <see cref="SlotId"/>
		/// </summary>
		/// <returns></returns>
		public virtual int GetSlotNumber() { return GetSlotNumber(SlotId); }

		/// <summary>
		/// Returns an informative string representation of this instance
		/// </summary>
    public override string ToString()
    {
      return string.Format("{0} ({1} - {2}): {3} {4}", SlotId, Sensor.SensorType, DisplayName, CurrentValue, Parameter.ValueLabel);
    }

		/// <summary>
		/// Returns the <see cref="SlotIdType"/> resembled by the supplied value
		/// </summary>
		/// <param name="slotIndex">the value to interpret as <see cref="SlotIdType"/></param>
    public static SlotIdType GetId(int slotIndex)
    {
        return (SlotIdType)Enum.Parse(typeof(SlotIdType), string.Format("Slot{0}", slotIndex));
    }

		static readonly Regex IntParseRegex = new Regex(@"(?in)(?<int>\d+)$");
		/// <summary>
		/// Returns the numerical representation of the supplied slotIndex
		/// </summary>
		public static int GetSlotNumber(SlotIdType slotId)
		{
			return int.Parse(IntParseRegex.Match(slotId.ToString()).Groups["int"].Value);
		}

	}
}
